package compiler._nfa;

import compiler.input.Input;

import java.util.*;


public class NfaInterpreter {
    public boolean debug = true;
    private Nfa start;
    private Input input;

    public NfaInterpreter(Nfa start, Input input) {
        this.start = start;
        this.input = input;
    }

    public Set<Nfa> e_closure(Set<Nfa> nfaSet) {
        /*
         * 计算input集合中nfa节点所对应的ε闭包，
    	 * 并将闭包的节点加入到nfaSet中
    	 */
        if (debug)
            System.out.print("ε-Closure( " + strFromNfaSet(nfaSet) + " ) = ");


        Stack<Nfa> nfaStack = new Stack<>();
        if (nfaSet == null || nfaSet.isEmpty()) {
            return null;
        }

        for (Nfa nfa : nfaSet) {
            nfaStack.add(nfa);
        }

        while (!nfaStack.empty()) {
            Nfa p = nfaStack.pop();

            if (p.next != null && p.getEdge() == Nfa.EPSILON) {
                if (!nfaSet.contains(p.next)) {
                    nfaStack.push(p.next);
                    nfaSet.add(p.next);
                }
            }

            if (p.next2 != null && p.getEdge() == Nfa.EPSILON) {
                if (!nfaSet.contains(p.next2)) {
                    nfaStack.push(p.next2);
                    nfaSet.add(p.next2);
                }
            }
        }

        if (debug) {
            System.out.println("{ " + strFromNfaSet(nfaSet) + " }");
        }

        return nfaSet;
    }

    private String strFromNfaSet(Set<Nfa> input) {
        String s = "";
        Iterator it = input.iterator();
        while (it.hasNext()) {
            s += ((Nfa) it.next()).getStateNum();
            if (it.hasNext()) {
                s += ",";
            }
        }

        return s;
    }

    public Set<Nfa> move(Set<Nfa> nfaSet, char inputChar) {
        Set<Nfa> outSet = new HashSet<>();

        for (Nfa nextNfa : nfaSet) {
            Set<Byte> inputSet = nextNfa.inputSet;
            Byte cb = (byte) inputChar;
            //字符集合
            boolean contains = inputSet.contains(cb);
            boolean edgeCCL = nextNfa.getEdge() == Nfa.CCL;
            //单个字符情况
            boolean equals = Objects.equals(nextNfa.getEdge(), inputChar);

            if (equals || ((edgeCCL && contains))) {
                outSet.add(nextNfa.next);
            }
        }

        if (debug) {
            System.out.print("move({ " + strFromNfaSet(nfaSet) + " }, '" + inputChar + "')= ");
            System.out.println("{ " + strFromNfaSet(outSet) + " }");
        }

        return outSet;
    }

    public void interpretNfa() {
        //从控制台读入要解读的字符串
        System.out.println("Input string: ");

        input.readInput();

        Set<Nfa> nextNfaSet = new HashSet<>();
        nextNfaSet.add(start);
        nextNfaSet = e_closure(nextNfaSet);

        Set<Nfa> current;
        int inputChar;
        boolean lastAccepted = false;

        while (!Objects.equals(inputChar = input.advance(), Input.EOF)) {
            current = move(nextNfaSet, (char) inputChar);
            if (current.size() == 0) {
                continue;
            }
            nextNfaSet = e_closure(current);

            if (hasAcceptState(nextNfaSet)) {
                lastAccepted = true;
                break;
            }
        }

        if (lastAccepted) {
            System.out.println("The Nfa Machine can recognize string");
        }
    }

    private boolean hasAcceptState(Set<Nfa> input) {
        boolean isAccepted = false;
        if (input == null || input.isEmpty()) {
            return false;
        }

        String acceptedStatement = "Accept State: ";
        for (Nfa p : input) {
            if (p.next == null && p.next2 == null) {
                isAccepted = true;
                acceptedStatement += p.getStateNum() + " ";
            }
        }

        if (isAccepted) {
            System.out.println(acceptedStatement);
        }

        return isAccepted;
    }
}
